import json
import os
import time
import re
from typing import List, Dict, Any, Tuple
import google.generativeai as genai

# 从配置文件导入所有常量和规则
from config import (
    GEMINI_MODEL_NAME, NON_DRUG_KEYWORDS, DATE_REGEX,
    UNCERTAIN_DIAGNOSIS_KEYWORDS, CHRONIC_DISEASES, SCORING_RULES
)


class MedicalRecordQC:
    """病历质量控制智能体，封装了完整的分析流程。"""

    def __init__(self, api_key: str):
        genai.configure(api_key=api_key)
        try:
            self.model = genai.GenerativeModel(GEMINI_MODEL_NAME)
            print("Gemini Pro 模型初始化成功！")
        except Exception as e:
            print(f"模型初始化失败，请检查你的API密钥或网络连接: {e}")
            raise

    def run_analysis(self, record_data: Dict[str, Any]) -> Tuple[int, List[Dict[str, str]]]:
        """
        执行完整的三阶段质控分析流程。
        """
        all_issues = []

        # 步骤 1: Agent 1 - 数据规范化与基础检查
        normalized_data, precheck_issues = self._normalize_and_precheck(record_data)
        all_issues.extend(precheck_issues)

        # 步骤 2: Agent 2 - 确定性规则检查
        deterministic_issues = self._run_deterministic_checks(normalized_data)
        all_issues.extend(deterministic_issues)

        # 步骤 3: Agent 3 - 内容质量AI评估
        ai_quality_issues = self._run_ai_quality_checks(normalized_data)
        all_issues.extend(ai_quality_issues)

        # 计算最终得分
        final_score = 100
        for issue in all_issues:
            final_score -= SCORING_RULES.get(issue['rule_id'], 0)

        return max(0, final_score), all_issues

    def _normalize_and_precheck(self, record_data: Dict[str, Any]) -> Tuple[Dict[str, Any], List[Dict[str, str]]]:
        issues_found: List[Dict[str, str]] = []
        normalized_record = json.loads(json.dumps(record_data))
        standardized_medication: List[Dict[str, Any]] = []
        if '用药信息' in normalized_record and normalized_record['用药信息']:
            for index, med in enumerate(normalized_record['用药信息']):
                standard_med = {'drug_name': None, 'specification': None, 'usage': None, 'dose': None, 'days': None,
                                'total_quantity': None}
                if '名称' in med:
                    standard_med.update({'drug_name': med.get('名称'), 'specification': med.get('规格'),
                                         'usage': med.get('用法') or med.get('Sig'),
                                         'dose': med.get('剂量') or med.get('每次量'), 'days': med.get('天数'),
                                         'total_quantity': med.get('总量')})
                elif '药品名称' in med:
                    standard_med.update(
                        {'drug_name': med.get('药品名称'), 'specification': med.get('规格'), 'usage': med.get('用法'),
                         'dose': med.get('每次用量'), 'days': med.get('用药天数'), 'total_quantity': med.get('总量')})
                else:
                    issues_found.append(
                        {'rule_id': 'S-1', 'description': f"用药信息中第 {index + 1} 项的结构不统一或无法识别: {med}"})
                standardized_medication.append(standard_med)
        normalized_record['用药信息'] = standardized_medication
        for med in normalized_record['用药信息']:
            if med.get('drug_name') and any(keyword in med['drug_name'] for keyword in NON_DRUG_KEYWORDS):
                issues_found.append(
                    {'rule_id': 'S-3', 'description': f"用药信息中被识别为非药品项目: '{med['drug_name']}'"})
        visit_date = normalized_record.get('患者信息', {}).get('就诊日期')
        if visit_date and (not isinstance(visit_date, str) or not DATE_REGEX.match(visit_date)):
            issues_found.append({'rule_id': 'S-5', 'description': f"患者信息的'就诊日期'格式不规范: '{visit_date}'"})
        return normalized_record, issues_found

    def _run_deterministic_checks(self, record_data: Dict[str, Any]) -> List[Dict[str, str]]:
        issues_found: List[Dict[str, str]] = []
        patient_info, condition_info, doctor_info = record_data.get('患者信息', {}), record_data.get('病情信息',
                                                                                                     {}), record_data.get(
            '医生信息', {})
        for key in ['姓名', '年龄', '性别']:
            if not patient_info.get(key): issues_found.append(
                {'rule_id': 'C-1', 'description': f"患者信息中缺少关键字段: '{key}'"})
        for key in ['主诉', '现病史', '体格检查', '诊断']:
            if not condition_info.get(key): issues_found.append(
                {'rule_id': 'C-2', 'description': f"病情信息中缺少核心字段: '{key}'"})
        if not doctor_info.get('医生签名'): issues_found.append(
            {'rule_id': 'C-3', 'description': "医生信息中缺少'医生签名'"})
        if condition_info.get('主诉') and len(condition_info['主诉']) < 5: issues_found.append(
            {'rule_id': 'C-4', 'description': f"主诉内容过短 (少于5个字符)"})
        if condition_info.get('现病史') and len(condition_info['现病史']) < 20: issues_found.append(
            {'rule_id': 'C-4', 'description': f"现病史内容过短 (少于20个字符)"})
        if not patient_info.get('联系电话'): issues_found.append(
            {'rule_id': 'C-5', 'description': "患者信息中缺少'联系电话'"})
        diagnosis = condition_info.get('诊断', '')
        if diagnosis and any(keyword in diagnosis for keyword in UNCERTAIN_DIAGNOSIS_KEYWORDS): issues_found.append(
            {'rule_id': 'S-2', 'description': f"诊断描述不确定: '{diagnosis}'"})
        for med in record_data.get('用药信息', []):
            dose, total = med.get('dose'), med.get('total_quantity')
            if isinstance(dose, str) and isinstance(total, str) and '片' in dose and 'mg' in total:
                issues_found.append({'rule_id': 'S-4',
                                     'description': f"药品'{med.get('drug_name')}'的单次剂量(片)与总量(mg)单位冲突，可能存在逻辑错误"})
        gender, past_history = patient_info.get('性别'), condition_info.get('既往史', '')
        if gender == '男性' and '月经史' in condition_info and condition_info.get('月经史') not in ['-', '无', None,
                                                                                                    '']:
            issues_found.append({'rule_id': 'L-1', 'description': "性别为男性，但存在月经史记录"})
        if diagnosis:
            for disease in CHRONIC_DISEASES:
                if disease in diagnosis and disease not in past_history:
                    issues_found.append(
                        {'rule_id': 'L-4', 'description': f"诊断中提及'{disease}'，但在既往史中未找到相应记录"})
        return issues_found

    def _run_ai_quality_checks(self, record_data: Dict[str, Any]) -> List[Dict[str, str]]:
        """
        Agent 3 的核心功能: 执行需要LLM进行内容理解的质量规则检查，
        并在不合格时获取修改建议和示例 (已扩展至Q1-Q5)。
        """
        issues_found: List[Dict[str, Any]] = []
        condition_info = record_data.get('病情信息', {})

        # --- Helper function to parse LLM response ---
        def parse_llm_response(response: str) -> Tuple[str, Dict[str, str]]:
            lines = response.split('\n', 1)
            status = lines[0].strip()
            details = {'reason': 'N/A', 'suggestion': 'N/A', 'example': 'N/A'}

            if status.startswith("不合格") and len(lines) > 1:
                details_text = lines[1].strip()
                reason_match = re.search(r"理由：(.*)", details_text)
                suggestion_match = re.search(r"修改建议：(.*)", details_text)
                example_match = re.search(r"修改示例：(.*)", details_text, re.DOTALL)  # re.DOTALL 允许多行示例

                if reason_match: details['reason'] = reason_match.group(1).strip()
                if suggestion_match: details['suggestion'] = suggestion_match.group(1).strip()
                if example_match: details['example'] = example_match.group(1).strip()

                # Fallback if structured parsing fails
                if details['reason'] == 'N/A' and details_text:
                    details['reason'] = details_text

            return status, details

        # --- Base Prompt Template ---
        prompt_template = """
        **评估标准**: {standard}
        **待评估文本**: "{text}"
        **评估任务**:
        1. 请首先判断上述“待评估文本”是否符合“评估标准”？请在第一行只回答“合格”或“不合格”。
        2. 如果第一行回答是“不合格”，请从第二行开始，严格按以下格式提供详细信息（每个标签占一行）：
           理由：[请简要说明为什么不合格]
           修改建议：[请提供具体的修改建议]
           修改示例：[请提供一个符合标准的修改后的文本示例]
        """

        # --- 规则 Q-1: 主诉内容质量 ---
        chief_complaint = condition_info.get('主诉')
        if chief_complaint:
            standard = "主诉需“简明扼要地描述患者本次就诊的主要症状（或体征）及其持续时间”。"
            prompt = prompt_template.format(standard=standard, text=chief_complaint)
            response = self._call_gemini_llm(prompt)
            status, details = parse_llm_response(response)
            if status.startswith("不合格"):
                issues_found.append({
                    'rule_id': 'Q-1',
                    'description': f"主诉内容质量不合规。AI分析理由: {details['reason']}",
                    'ai_suggestion': details['suggestion'],
                    'ai_example': details['example']
                })

        # --- 规则 Q-2: 现病史内容质量 ---
        present_history = condition_info.get('现病史')
        if present_history:
            standard = "现病史需“详细记录患者本次疾病的发生、发展、演变过程”。"
            prompt = prompt_template.format(standard=standard, text=present_history)
            response = self._call_gemini_llm(prompt)
            status, details = parse_llm_response(response)
            if status.startswith("不合格"):
                issues_found.append({
                    'rule_id': 'Q-2',
                    'description': f"现病史内容质量不合规。AI分析理由: {details['reason']}",
                    'ai_suggestion': details['suggestion'],
                    'ai_example': details['example']
                })

        # --- 规则 Q-3: 体格检查内容质量 ---
        physical_exam = condition_info.get('体格检查')
        if physical_exam:
            standard = "体格检查需“按照系统顺序进行全面检查并记录”，描述不能过于简单。"
            prompt = prompt_template.format(standard=standard, text=physical_exam)
            response = self._call_gemini_llm(prompt)
            status, details = parse_llm_response(response)
            if status.startswith("不合格"):
                issues_found.append({
                    'rule_id': 'Q-3',
                    'description': f"体格检查内容质量不合规。AI分析理由: {details['reason']}",
                    'ai_suggestion': details['suggestion'],
                    'ai_example': details['example']
                })

        # --- 规则 Q-4: 诊断内容质量 ---
        diagnosis = condition_info.get('诊断')
        if diagnosis:
            # 排除掉确定性规则S-2已经标记的不确定诊断，避免重复评估
            if not any(keyword in diagnosis for keyword in UNCERTAIN_DIAGNOSIS_KEYWORDS):
                standard = "诊断需“根据患者的症状、体征和检查结果，提出最可能的疾病诊断”，应明确具体。"
                prompt = prompt_template.format(standard=standard, text=diagnosis)
                response = self._call_gemini_llm(prompt)
                status, details = parse_llm_response(response)
                if status.startswith("不合格"):
                    issues_found.append({
                        'rule_id': 'Q-4',
                        'description': f"诊断内容质量不合规。AI分析理由: {details['reason']}",
                        'ai_suggestion': details['suggestion'],
                        'ai_example': details['example']
                    })

        # --- 规则 Q-5: 过敏史/既往史内容质量 ---
        # 我们将过敏史和既往史合并检查，因为它们的标准类似
        allergy_history = condition_info.get('过敏史')
        past_history = condition_info.get('既往史')
        history_text = f"过敏史: {allergy_history}\n既往史: {past_history}"

        if allergy_history or past_history:  # 只要有一个存在就检查
            # 避免对非常简短的 "无" 或 "否认" 进行不必要的AI评估
            if len(history_text) > 15:
                standard = "过敏史和既往史的记录不能过于含糊（如仅写‘无特殊’），应能清晰反映患者相关的健康状况或风险因素。"
                prompt = prompt_template.format(standard=standard, text=history_text)
                response = self._call_gemini_llm(prompt)
                status, details = parse_llm_response(response)
                if status.startswith("不合格"):
                    issues_found.append({
                        'rule_id': 'Q-5',
                        'description': f"过敏史/既往史内容质量不合规。AI分析理由: {details['reason']}",
                        'ai_suggestion': details['suggestion'],
                        'ai_example': details['example']
                    })

        return issues_found

    def _call_gemini_llm(self, prompt: str) -> str:
        # (这是之前的 call_gemini_llm 函数，现在是类的一个私有方法)
        print(f"\n[调用 Gemini API] 正在发送请求至AI进行分析...")
        try:
            response = self.model.generate_content(prompt)
            time.sleep(1)
            print("[调用成功] AI已返回分析结果。")
            return response.text.strip()
        except Exception as e:
            print(f"  [API 调用失败] 发生错误: {e}")
            return "API_ERROR: 无法获取AI分析结果。"


# ==============================================================================
# 主流程
# ==============================================================================
if __name__ == '__main__':
    # 1. 初始化 QC Agent
    API_KEY = os.getenv("GOOGLE_API_KEY")
    if not API_KEY:
        raise ValueError("API key not found. Please set the GOOGLE_API_KEY environment variable.")

    qc_agent = MedicalRecordQC(api_key=API_KEY)
    CURRENT_FILE_PATH = os.path.abspath(__file__)
    SRC_DIR = os.path.dirname(CURRENT_FILE_PATH)
    PROJECT_ROOT = os.path.dirname(SRC_DIR)
    records_directory = os.path.join(PROJECT_ROOT, 'records')
    if not os.path.isdir(records_directory):
        print(f"错误: 请在脚本同目录下创建 '{records_directory}' 文件夹，并放入JSON文件。")
    else:
        json_files = [f for f in os.listdir(records_directory) if f.endswith('.json')]
        file_count = len(json_files)

        for i, file_name in enumerate(json_files):
            file_path = os.path.join(records_directory, file_name)
            print(f"\n{'=' * 25} 正在生成 {file_name} 的质控报告 {'=' * 25}")

            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    medical_record = json.load(f)

                # 3. 运行分析并获取结果
                final_score, all_issues = qc_agent.run_analysis(medical_record)

                # 4. 生成报告
                print("\n--- 📝 病历质量评估报告 ---")
                if not all_issues:
                    print("✅ 经全面检查，此病历质量较高，未发现明显问题。")
                else:
                    print("❌ 此病历存在以下问题:")
                    for issue in all_issues:
                        rule_id = issue['rule_id']
                        score_deduction = SCORING_RULES.get(rule_id, 0)
                        # 打印基础问题描述 (理由)
                        print(f"  - [扣{score_deduction}分] [规则 {rule_id}]: {issue['description']}")
                        # 检查并打印 AI 提供的建议和示例 (如果存在且不为'N/A')
                        if issue.get('ai_suggestion') and issue['ai_suggestion'] != 'N/A':
                            print(f"    💡 修改建议: {issue['ai_suggestion']}")
                        if issue.get('ai_example') and issue['ai_example'] != 'N/A':
                            print(f"    ✍️ 修改示例: {issue['ai_example']}")
                print(f"\n--- 💯 最终得分: {final_score} ---")

            except json.JSONDecodeError:
                print(f"错误: 文件 {file_name} 不是一个有效的JSON文件，已跳过。")
            except Exception as e:
                print(f"处理文件 {file_name} 时发生严重错误: {e}")
